﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
#if USE_WIF
using Microsoft.IdentityModel.Protocols.WSFederation;
using Microsoft.IdentityModel.Web;
using SSP = System.Security.Principal;
#endif
using SC = System.Configuration;
using SNM = System.Net.Mail;
using CodeCamp.Infrastructure;
using CodeCamp.Models;
using CCM = CodeCamp.Model;
using CCMS = CodeCamp.Models.Shared;
using RGoodSW.Logger.Interfaces;
using RGoodSW.Logger.MvcHelper.Models;
#if USE_WIF
using CodeCamp.Models.Account;
#endif

namespace CodeCamp.Controllers
{
    public class AccountController : ControllerBase
    {
        private static class Tags
        {
            internal const string AcctResetCount = "AcctResetCount";
            internal const string acctResetFromEmail = "acctResetFromEmail";
            internal const string acctResetFromEmailName = "acctResetFromEmailName";
            internal const string acctResetHostSubstitute = "acctResetHostSubstitute"; //format of value is "oldhost.foo.com=newhost.bar.com"
        }

        public AccountController(IDefaultValueProvider defaultValueProvider, ILoggerExtended logger)
            : base(defaultValueProvider, logger)
        {
        }


#if (USE_WIF && USE_MEMBERSHIP)
    #error DO_NOT_USE_BOTH_WIF_AND_MEMBERSHIP
#endif
        //
        // GET: /Account/LogOn

#if USE_WIF
        //[RequireHttps] //Uncomment after cert acquired
        [RequireSSLForAzureWebService]
        public ActionResult SignIn(string eventName)
        {
            if (User.Identity.IsAuthenticated)
                return RedirectToAction("Index", "Home");

            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                if (!ccEvent.ShortName.Equals(eventName, StringComparison.CurrentCultureIgnoreCase))
                    return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });
                SetSharedViewBagProperties(ccEvent);

                WIFLogin model = new WIFLogin();
                Nullable<CodeCamp.FederatedAuthConfig.PlatformType> platformType;
                string wifConfig = FederatedAuthConfig.GetWIFConfigName(DefaultValueProvider, out platformType);
                if (platformType.HasValue)
                    model.Platform = platformType.Value;

                model.IdentityRealm = Server.UrlEncode(FederatedAuthConfig.GetWIFRealm(DefaultValueProvider, platformType));
                SetLoggerThreadContextData();
                if (string.IsNullOrEmpty(wifConfig) || string.IsNullOrEmpty(model.IdentityRealm))
                    Logger.WarnFormat("SignIn wifConfig=\"{0}\", platformType={1}, identityRealm=\"{2}\"", wifConfig, platformType, model.IdentityRealm);
                else
                    Logger.InfoFormat("SignIn wifConfig=\"{0}\", platformType={1}, identityRealm=\"{2}\"", wifConfig, platformType, model.IdentityRealm);
                return View("SignInWif", model);
            }
        }

        [CustomAuthorize]
        public ActionResult SignedIn(string eventName)
        {
            using (var ccdr = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(ccdr, eventName, DefaultEventShortName);
                if (!ccEvent.ShortName.Equals(eventName, StringComparison.CurrentCultureIgnoreCase))
                {
                    Logger.InfoFormat("SignedIn - event Id={0}, name={1}.", ccEvent.Id, ccEvent.ShortName);
                    return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });
                }

                CCMS.CommonViewModel model = new CCMS.CommonViewModel();
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, model, ccdr, UserIsAuthenticated(this.User), true);
                //SetLoggerThreadContextData now called from SetSharedEventProperties

                Logger.InfoFormat("SignedIn - event Id={0}, name={1}, siteUserId={2}.", 
                    ccEvent.Id, ccEvent.ShortName, this.SiteUserId);

                if ((SiteUserId.HasValue) && (SiteUserId.Value != 0))
                    return RedirectToAction("Index", "Home");
                return RedirectToAction("RegisterUser");
            }
        }


        public ActionResult SignOut(string eventName)
        {
            SetLoggerThreadContextData();
            string identityProvider = null;
            if (Request.IsAuthenticated)
            {
                using (var ccdr = new CodeCampDataRepository())
                {
                    var ccEvent = EventsHelper.GetEvent(ccdr, eventName, DefaultEventShortName);
                    CCMS.CommonViewModel model = new CCMS.CommonViewModel();
                    SetSharedViewBagProperties(ccEvent);
                    SetSharedEventProperties(ccEvent, model, ccdr, UserIsAuthenticated(this.User));

                    if (TempData.ContainsKey("identityProvider"))
                        TempData.Remove("identityProvider");
                    TempData.Add("identityProvider", identityProvider);

                    Logger.Info("SignOut (1)");
                    FederatedAuthentication.WSFederationAuthenticationModule.SignOut(false);
                    return RedirectToAction("SignOut");
                }
            }
            if (TempData.ContainsKey("identityProvider"))
            {
                identityProvider = TempData["identityProvider"] as string;
                if (!string.IsNullOrWhiteSpace(identityProvider))
                {
                    ViewData["identityProvider"] = identityProvider;
                }
            }
            var issuer = FederatedAuthentication.WSFederationAuthenticationModule.Issuer;
            var signout = new SignOutRequestMessage(new Uri(issuer));
            var url = signout.WriteQueryString();
            ViewData["STSSignout"] = url;
            Logger.InfoFormat("SignOut (2) provider = \"{1}\" {0} issuer = \"{2}\" {0} STSSignout = \"{3}\" {0}",
                Environment.NewLine, identityProvider, issuer, url);
            return View("SignedOut");
        }

#endif

        [CustomAuthorize]
        public ActionResult RegisterUser(string eventName)
        {
            SetLoggerThreadContextData();

            using (var ccdr = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(ccdr, eventName, DefaultEventShortName);
                if (!ccEvent.ShortName.Equals(eventName, StringComparison.CurrentCultureIgnoreCase))
                    return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });
                SetSharedViewBagProperties(ccEvent);
                UserRegisterModel model = new UserRegisterModel();
                
#if USE_WIF
                string identityProvider;
                FillViewModelFromDbOrClaims(ccdr, ccEvent, this.User, UserIsAuthenticated(this.User), model, out identityProvider);
                Logger.DebugFormat("RegisterUser - RegisteredSiteUserId={0}, email=\"{1}\", identityProvider=\"{2}\"",
                    model.RegisteredSiteUserId,
                    model.Email,
                    identityProvider);
                if (!model.RegisteredSiteUserId.HasValue)
                    return RedirectToAction("Index", "Home");

                if ((model.RegisteredSiteUserId.HasValue) && (model.RegisteredSiteUserId.Value != 0))
                {
                    //return RedirectToAction("Edit");
                    return RedirectToAction("Index", "Home");
                }
#endif
#if USE_MEMBERSHIP
                MembershipUser memUser = Membership.GetUser(User.Identity.Name);
                if (memUser != null)
                {
                    model.Email = memUser.Email;
                }
                model.RegisteredSiteUserId = 0;
#endif

                return View(model);
            }
        }

        [CaptchaMvc.Attributes.CaptchaVerify("Entry does not match captch image.")]
        [HttpPost]
        [CustomAuthorize]
        public ActionResult RegisterUser(string eventName, UserRegisterModel model)
        {
            SetLoggerThreadContextData();
            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                if (!ccEvent.ShortName.Equals(eventName, StringComparison.CurrentCultureIgnoreCase))
                    return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });
                SetSharedViewBagProperties(ccEvent);

                if (ModelState.IsValid)
                {
#if USE_WIF
                    bool newSiteUserIsValid;
                    CCM.SiteUser siteUser = FillNewSiteUser(model, this.User, out newSiteUserIsValid);
                    if (newSiteUserIsValid)
                    {
                        siteUser.IsApproved = true; //Can defer this if email verification added
                        siteUser.IpAtSignup = this.Request.UserHostAddress;
                        repository.EFContext.SiteUsers.Add(siteUser);
                        repository.Save();
                        return RedirectToAction("Index", "Home");
                    }
                    ModelState.AddModelError("", "Internal error creating registered user.");
                    Logger.Error("FillNewSiteUser returned newSiteUserIsValid = false");
#endif
#if USE_MEMBERSHIP
                    CCM.SiteUser siteUser = new CCM.SiteUser
                    {
                        CreatedBy = model.Email,
                        EmailAddress = model.Email,
                        FirstName = model.FirstName,
                        IpAtSignup = Request.UserHostAddress,
                        IsApproved = true,
                        IsLockedOut = false,
                        LastLoginUTC = DateTime.UtcNow,
                        LastName = model.LastName,
                        LastOnlineActivityUTC = DateTime.UtcNow
                    };

                    MembershipUser memUser = Membership.GetUser(User.Identity.Name);
                    if (memUser != null)
                    {
                        siteUser.UserClaimEmailAddress = memUser.Email;
                        siteUser.UserIdentityClaim = memUser.UserName;
                        siteUser.UserIdentityClaimType = CodeCampDataRepository.ClaimTypes.username;
                        siteUser.UserIdentityIssuer = CodeCampDataRepository.IdentityIssuers.CodeCamp;
                        siteUser.UserIdentityProvider = CodeCampDataRepository.IdentityProviders.CodeCamp;
                        repository.EFContext.SiteUsers.Add(siteUser);
                        repository.Save();
                        return RedirectToAction("Index", "Home");
                    }
#endif
                }
                else
                {
                    Logger.Warn("RegisterUser submission failed.");
                }
                return View(model);
            }
        }

#if USE_MEMBERSHIP

        public ActionResult SignIn(string eventName)
        {
            if (User.Identity.IsAuthenticated)
                return RedirectToAction("Index", "Home");

            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                if (!ccEvent.ShortName.Equals(eventName, StringComparison.CurrentCultureIgnoreCase))
                    return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });
                LogOnModel model = new LogOnModel();
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, model, repository);

                return View(model);
            }
        }

        //private void CheckAndCreateRolls(string username)
        //{
        //    SetLoggerThreadContextData();
        //    try
        //    {
        //        //TODO: Check this when adding claims auth
        //        if (Roles.Enabled)
        //        {
        //            if (!Roles.RoleExists(CCMS.CCRoles.RoleNames.Presenter))
        //            {
        //                Roles.CreateRole(CCMS.CCRoles.RoleNames.Presenter);
        //            }
        //            if (!Roles.RoleExists(CCMS.CCRoles.RoleNames.ContentEditor))
        //            {
        //                Roles.CreateRole(CCMS.CCRoles.RoleNames.ContentEditor);
        //            }
        //            if (!Roles.RoleExists(CCMS.CCRoles.RoleNames.BigCheeze))
        //            {
        //                Roles.CreateRole(CCMS.CCRoles.RoleNames.BigCheeze);
        //            }
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        Logger.ErrorFormat(ex,
        //            "{1} checking/creating roles for user \"{0}\": {2}", username, ex.GetType(), ex.Message);
        //        string foo = ex.Message;
        //    }
        //}

#if false
        private HttpCookie CreateAuthTicket(LogOnModel model, CodeCampDataRepository ccdr, int eventId, CCM.SiteUser siteUser)
        {
            return CreateAuthTicket(model.UserName, model.RememberMe, ccdr, eventId, siteUser);
        }

        private HttpCookie CreateAuthTicket(RegisterModel model, CodeCampDataRepository ccdr, int eventId, CCM.SiteUser siteUser)
        {
            return CreateAuthTicket(model.UserName, false, ccdr, eventId, siteUser);
        }

        private HttpCookie CreateAuthTicket(string username, bool rememberMe, CodeCampDataRepository ccdr, int eventId, CCM.SiteUser siteUser)
        {
            System.Text.StringBuilder roles = new System.Text.StringBuilder();
            List<string> siteUserRoles = GetSiteUserRoles(ccdr, siteUser.Id, eventId);

            foreach (string role in siteUserRoles)
            {
                if (roles.Length > 0)
                    roles.Append('|');
                roles.Append(role);
            }

            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
                username,
                DateTime.Now,
                DateTime.Now.AddMinutes(15),
                rememberMe,
                roles.ToString());
            string encodedTicket = FormsAuthentication.Encrypt(ticket);
            HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encodedTicket);
            return authCookie;
        }

        private void SetAuthCookie(HttpCookie authCookie)
        {
            if (Response.Cookies.AllKeys.Contains(FormsAuthentication.FormsCookieName))
            {
                Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
            }
            Response.Cookies.Add(authCookie);
        }
#endif

        //
        // POST: /Account/LogOn

        [HttpPost]
        public ActionResult SignIn(string eventName, LogOnModel model, string returnUrl)
        {
            SetLoggerThreadContextData();

            using (var ccdr = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(ccdr, eventName, DefaultEventShortName);
                if (!ccEvent.ShortName.Equals(eventName, StringComparison.CurrentCultureIgnoreCase))
                    return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });
            
                if (ModelState.IsValid)
                {
                    if (Membership.ValidateUser(model.UserName, model.Password))
                    {
#if false
                        CCM.SiteUser siteUser = GetSiteUserAtLogon(ccdr, model.UserName);
                        if ((siteUser == null) || (siteUser.Id == 0))
                        {
                            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                            return RedirectToAction("RegisterUser");
                        }
                        HttpCookie authCookie = CreateAuthTicket(model, ccdr, ccEvent.Id, siteUser);
                        SetAuthCookie(authCookie);
#else
                        FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
#endif

                        Nullable<int> siteUserId = null;
                        CCM.SiteUser siteUser = GetSiteUser(ccdr, model.UserName, true, true);
                        if (siteUser != null)
                            siteUserId = siteUser.Id;
                        //SetLoggerThreadContextData now called from SetSharedEventProperties

                        Logger.InfoFormat("SignIn - event Id={0}, name={1}, siteUserId={2}.",
                            ccEvent.Id, ccEvent.ShortName, siteUserId);

                        if ((!siteUserId.HasValue) || (siteUserId.Value == 0))
                            return RedirectToAction("RegisterUser");

                        //CheckAndCreateRolls(model.UserName);

                        if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                            && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                        {
                            return Redirect(returnUrl);
                        }
                        else
                        {
                            return RedirectToAction("Index", "Home");
                        }
                    }
                    else
                    {
                        Logger.WarnFormat("SignIn Failed for \"{0}\".", model.UserName);
                        ModelState.AddModelError("", "The user name or password provided is incorrect.");
                    }
                }

                // If we got this far, something failed, redisplay form
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, model, ccdr);

                return View(model);
            }
        }

        //
        // GET: /Account/LogOff

        public ActionResult SignOut()
        {
            FormsAuthentication.SignOut();

            return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });
        }

        //
        // GET: /Account/Create



        public ActionResult Create(string eventName, string email)
        {
            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                if (!ccEvent.ShortName.Equals(eventName, StringComparison.CurrentCultureIgnoreCase))
                    return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });

                RegisterModel model = new RegisterModel();
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, model, repository);

                return View(model);
            }
        }

        //
        // POST: /Account/Register

        [CaptchaMvc.Attributes.CaptchaVerify("Entry does not match captch image.")]
        [HttpPost]
        public ActionResult Create(string eventName, RegisterModel model)
        {
            SetLoggerThreadContextData();
            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                if (!ccEvent.ShortName.Equals(eventName, StringComparison.CurrentCultureIgnoreCase))
                    return RedirectToAction("Index", "Home", new { eventName = DefaultEventShortName });

                if (ModelState.IsValid)
                {
                    // Attempt to register the user
                    MembershipCreateStatus createStatus;
                    try
                    {
                        MembershipUser memUser = Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);
                        CCM.SiteUser siteUser = new CCM.SiteUser
                        {
                            CreatedBy = model.Email,
                            EmailAddress = model.Email,
                            FirstName = model.FirstName,
                            IpAtSignup = Request.UserHostAddress,
                            IsApproved = true,
                            IsLockedOut = false,
                            LastLoginUTC = DateTime.UtcNow,
                            LastName = model.LastName,
                            LastOnlineActivityUTC = DateTime.UtcNow
                        };
                        siteUser.UserClaimEmailAddress = memUser.Email;
                        siteUser.UserIdentityClaim = memUser.UserName;
                        siteUser.UserIdentityClaimType = CodeCampDataRepository.ClaimTypes.username;
                        siteUser.UserIdentityIssuer = CodeCampDataRepository.IdentityIssuers.CodeCamp;
                        siteUser.UserIdentityProvider = CodeCampDataRepository.IdentityProviders.CodeCamp;
                        repository.EFContext.SiteUsers.Add(siteUser);
                        repository.Save();

#if false
                        HttpCookie authCookie = CreateAuthTicket(model, repository, ccEvent.Id, siteUser);
                        SetAuthCookie(authCookie);
#else
                        FormsAuthentication.SetAuthCookie(model.UserName, false);
#endif

                        return RedirectToAction("Index", "Home");
                    }
                    catch (Exception ex)
                    {
                        Logger.ErrorFormat(ex, "{1} creating account for user \"{0}\": {2}", model.UserName, ex.GetType(), ex.Message);
                        createStatus = MembershipCreateStatus.ProviderError;
                    }

                    if (createStatus == MembershipCreateStatus.Success)
                    {
                        FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
                        //CheckAndCreateRolls(model.UserName);

                        return RedirectToAction("Index", "Home");
                    }
                    else
                    {
                        ModelState.AddModelError("", ErrorCodeToString(createStatus));
                    }
                }
                // If we got this far, something failed, redisplay form
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, model, repository);

                return View(model);
            }
        }

        //
        // GET: /Account/ChangePassword

        [CustomAuthorize]
        public ActionResult ChangePassword(string eventName)
        {
            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                ChangePasswordModel model = new ChangePasswordModel();
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, model, repository);

                return View(model);
            }
        }

        //
        // POST: /Account/ChangePassword

        [CustomAuthorize]
        [HttpPost]
        public ActionResult ChangePassword(ChangePasswordModel model, string eventName)
        {
            SetLoggerThreadContextData();
            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                if (ModelState.IsValid)
                {
                    // ChangePassword will throw an exception rather
                    // than return false in certain failure scenarios.
                    bool changePasswordSucceeded;
                    try
                    {
                        MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
                        changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
                    }
                    catch (Exception ex)
                    {
                        Logger.ErrorFormat(ex, "{1} changing password for user \"{0}\": {2}",
                            this.User.Identity.Name, ex.GetType(), ex.Message);
                        changePasswordSucceeded = false;
                    }

                    if (changePasswordSucceeded)
                    {
                        Logger.Info("User changed password.");
                        return RedirectToAction("ChangePasswordSuccess");
                    }
                    else
                    {
                        ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
                    }
                }
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, model, repository);

                // If we got this far, something failed, redisplay form
                return View(model);
            }
        }

        //
        // GET: /Account/ChangePasswordSuccess

        public ActionResult ChangePasswordSuccess()
        {
            return View();
        }

        public ActionResult ResetAcct(string eventName)
        {
            SetLoggerThreadContextData();
            Nullable<int> acctResetCount = null;
            if (TempData.ContainsKey(Tags.AcctResetCount))
                acctResetCount = TempData[Tags.AcctResetCount] as Nullable<int>;

            if (User.Identity.IsAuthenticated)
            {
                Logger.WarnFormat("Account/ResetAcct count={0} Authenticated user \"{1}\" attempted account reset.",
                    acctResetCount, User.Identity.Name);
                return RedirectToAction("Index", "Home");
            }

            if (!acctResetCount.HasValue)
            {
                acctResetCount = 1;
                TempData[Tags.AcctResetCount] = acctResetCount;
            }

            if (!Membership.Provider.EnablePasswordReset)
            {
                Logger.WarnFormat("Account/ResetAcct count={0}.  Reset not enabled.", acctResetCount);
                return RedirectToAction("Index", "Home");
            }

            Logger.WarnFormat("Account/ResetAcct attempt: {0}.", acctResetCount);

            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                ResetAcctViewModel model = new ResetAcctViewModel();
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, model, repository);

                if (acctResetCount.Value > acctResetMaxTryCount)
                {
                    acctResetCount = acctResetCount.Value + 1;
                    TempData[Tags.AcctResetCount] = acctResetCount;
                    ViewBag.AcctResetResultMsg = standardAcctResetResultFailMsg;
                }
                else
                {
                    model.ResetAcctModel = new ResetAcctModel();
                }

                return View(model);
            }
        }

        private int acctResetMaxTryCount = 8;
        private const string standardAcctResetResultFailMsg = "Your account cannot be reset at this time.";
        private const string standardAcctResetSuccessResultMsg = "Your password has been reset, and your new password has been emailed to you.";

        [CaptchaMvc.Attributes.CaptchaVerify("Entry does not match captch image.")]
        [HttpPost]
        public ActionResult ResetAcct(ResetAcctModel model, string eventName)
        {
            SetLoggerThreadContextData();
            ViewBag.AcctResetResultMsg = null;

            Nullable<int> acctResetCount = null;
            if (TempData.ContainsKey(Tags.AcctResetCount))
                acctResetCount = TempData[Tags.AcctResetCount] as Nullable<int>;

            if (User.Identity.IsAuthenticated)
            {
                Logger.ErrorFormat(null, "Account/ResetAcct POST (count = {0}).  Authenticated user \"{1}\" attempted account reset (POST!).",
                    acctResetCount, User.Identity.Name);
                return RedirectToAction("Index", "Home");
            }

            bool showForm = true;

            if (!acctResetCount.HasValue)
            {
                acctResetCount = 0;
                TempData[Tags.AcctResetCount] = acctResetCount;
            }

            Logger.WarnFormat("Account/ResetAcct POST (count = {0}). Username=\"{1}, Last Name = \"{2}\"",
                acctResetCount, model.UserName, model.LastName);

            using (var repository = new CodeCampDataRepository())
            {
                var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName);
                ResetAcctViewModel viewModel = new ResetAcctViewModel();
                SetSharedViewBagProperties(ccEvent);
                SetSharedEventProperties(ccEvent, viewModel, repository);
                System.Text.StringBuilder resultMsg = new System.Text.StringBuilder();

                if (Membership.Provider.RequiresQuestionAndAnswer)
                {
                    Logger.WarnFormat("Account/ResetAcct POST (count = {0}).  Disabled - RequiresQuestionAndAnswer = true. Username=\"{1}, Last Name = \"{2}\"",
                        acctResetCount, model.UserName, model.LastName);
                    showForm = false;
                }
                else
                {
                    MembershipUser user = Membership.GetUser(model.UserName);
                    if (acctResetCount.Value >= acctResetMaxTryCount)
                    {
                        ViewBag.AcctResetResultMsg = standardAcctResetResultFailMsg;
                        if (user != null)
                        {
                            user.IsApproved = false;
                            Membership.Provider.UpdateUser(user);
                            Logger.ErrorFormat(null, "Account/ResetAcct POST (count = {0}). Username=\"{1}\" has been unapproved! Last Name = \"{2}\"",
                                acctResetCount, model.UserName, model.LastName);
                        }
                    }
                    else
                    {
                        if (ModelState.IsValid)
                        {
                            if (user == null)
                            {
                                Logger.WarnFormat("Account/ResetAcct POST count={0} User with Username \"{1}\" not found.  Last name = {2}.",
                                    acctResetCount, model.UserName, model.LastName);
                                ViewBag.AcctResetResultMsg = standardAcctResetResultFailMsg;
                            }
                            else if (!user.IsApproved)
                            {
                                Logger.ErrorFormat(null, "Account/ResetAcct POST (count = {0}). Username=\"{1}\" is not approved! Last Name = \"{2}\"",
                                    acctResetCount, model.UserName, model.LastName);
                                showForm = false;
                            }
                            else
                            {
                                System.Text.StringBuilder logMsg = new System.Text.StringBuilder();
                                try
                                {
                                    CCM.SiteUser siteUser = GetSiteUser(repository, this.User, false);
                                    ResetAccount(model, user, siteUser, acctResetCount, ccEvent.ShortName, logMsg, resultMsg, ref showForm);
                                }
                                catch (Exception ex)
                                {
                                    if (logMsg.Length == 0)
                                        logMsg.AppendFormat("Account/ResetAcct POST (count = {0}).", acctResetCount);
                                    LogException(logMsg.ToString(), ex);
                                    showForm = false;
                                }
                            }
                        }
                    }
                }
                if (showForm)
                {
                    viewModel.ResetAcctModel = model;
                }
                else
                {
                    if (resultMsg.Length > 0)
                        ViewBag.AcctResetResultMsg = resultMsg.ToString();
                    else
                        ViewBag.AcctResetResultMsg = standardAcctResetResultFailMsg;
                }

                acctResetCount = acctResetCount.Value + 1;
                TempData[Tags.AcctResetCount] = acctResetCount;
                return View(viewModel);
            }
        }

        private void ResetAccount(ResetAcctModel model,
            MembershipUser user,
            CCM.SiteUser siteUser,
            Nullable<int> acctResetCount,
            string eventShortName,
            System.Text.StringBuilder logMsg,
            System.Text.StringBuilder resultMsg,
            ref bool showForm)
        {
            if ((siteUser != null)
                && !siteUser.LastName.Equals(model.LastName, StringComparison.InvariantCultureIgnoreCase))
            {
                Logger.WarnFormat("Account/ResetAcct POST count={0} Presenter with Username \"{1}\" was found.  Last name \"{2}\" does not match.",
                    acctResetCount, model.UserName, model.LastName);
                resultMsg.Length = 0; resultMsg.Append(standardAcctResetResultFailMsg);
            }
            else
            {
                SNM.MailAddress toAddress = GetToAddress(user, siteUser);
                SNM.MailAddress fromAddress = GetFromAddress();
                SNM.SmtpClient mailClient = new SNM.SmtpClient();

                logMsg.AppendFormat("Account/ResetAcct POST count={1} User with Username \"{2}\" Sending email... {0}",
                    Environment.NewLine,
                    acctResetCount,
                    user.UserName);

                resultMsg.AppendFormat("Your account has been reset on {1}. {0}",
                    Environment.NewLine,
                    Request.Url.Host.ToString());

                if (fromAddress != null)
                {
                    logMsg.AppendFormat("from \"{1}\" {0}", Environment.NewLine, fromAddress.Address);
                }

                if (toAddress != null)
                {
                    logMsg.AppendFormat("to \"{1}\" {0}", Environment.NewLine, toAddress.Address);
                }

                logMsg.AppendFormat("using mail host \"{1}\" {0}", Environment.NewLine, mailClient.Host);

                if ((toAddress == null) || (fromAddress == null) || (mailClient.Credentials == null))
                {
                    Logger.ErrorFormat(null, logMsg.ToString());
                    showForm = false;
                }
                else
                {
                    if (user.IsLockedOut)
                        user.UnlockUser();

                    string urlHost = Request.Url.Host.ToLower();
                    string hostSubstValues = SC.ConfigurationManager.AppSettings[Tags.acctResetHostSubstitute];
                    if (!string.IsNullOrWhiteSpace(hostSubstValues))
                    {
                        string[] hostSubst = null;
                        hostSubst = hostSubstValues.Split('=');
                        if ((hostSubst != null) && (hostSubst.Length != 2)
                            || string.IsNullOrWhiteSpace(hostSubst[0])
                            || string.IsNullOrWhiteSpace(hostSubst[0]))
                        {
                            hostSubst = null;
                        }
                        else
                        {
                            hostSubst[0] = hostSubst[0].ToLower().Trim();
                            hostSubst[1] = hostSubst[1].ToLower().Trim();
                            if (urlHost.Equals(hostSubst[0], StringComparison.InvariantCultureIgnoreCase))
                                urlHost = hostSubst[1];
                        }
                    }
                    string hostPort = "";
                    if ((Request.Url.Port != 80) && (Request.Url.Port != 443))
                        hostPort = string.Format(":{0}", Request.Url.Port);

                    string newpwd = user.ResetPassword();
                    System.Text.StringBuilder messageBody = new System.Text.StringBuilder();
                    messageBody.AppendFormat("Your account on {1} has been reset.  The new password is \"{2}\". {0}",
                        Environment.NewLine,
                        urlHost,
                        newpwd);


                    RouteValueDictionary rvdict = new RouteValueDictionary();
                    //rvdict.Add("controller", "Presenter");
                    rvdict.Add("action", "ChangePassword");
                    rvdict.Add("eventName", eventShortName);
                    string changePwUrl = string.Format("http://{0}{1}{2}", urlHost, hostPort, Url.RouteUrl(rvdict));
                    Logger.DebugFormat("ChangePW URL: \"{0}\"", changePwUrl);

                    messageBody.AppendFormat("{0}After you log in with your new password, you can change it here: {0}{1} {0}",
                        Environment.NewLine,
                        changePwUrl);

                    if (siteUser != null)
                    {
                        RouteValueDictionary rvdict2 = new RouteValueDictionary();
                        rvdict2.Add("controller", "Presenters");
                        rvdict2.Add("action", "Details");
                        rvdict2.Add("eventName", eventShortName);
                        rvdict2.Add("id", siteUser.Id);
                        string presenterDetailUrl = string.Format("http://{0}{1}{2}", urlHost, hostPort, Url.RouteUrl(rvdict2));
                        Logger.DebugFormat("presenter detail URL: \"{0}\"", presenterDetailUrl);

                        messageBody.AppendFormat("{0}When you are logged in, you will also find a change password link on your Presenter Details page, here: {0}{1} {0}",
                            Environment.NewLine,
                            presenterDetailUrl);
                    }

                    //Send email!
                    bool invalidToEmailAddress;
                    if (SendEmail(mailClient, fromAddress, toAddress, null, messageBody, "Account/ResetAcct POST",
                        out invalidToEmailAddress))
                    {
                        logMsg.AppendLine("Sent. ");
                        Logger.Info(logMsg.ToString());

                        if (!string.IsNullOrWhiteSpace(fromAddress.DisplayName))
                        {
                            resultMsg.AppendFormat("Look for an email from {2} ({1}) with further information. {0}",
                                Environment.NewLine, fromAddress.Address, fromAddress.DisplayName);
                        }
                        else
                        {
                            resultMsg.AppendFormat("Look for an email from {1} with further information. {0}",
                                Environment.NewLine, fromAddress.Address);
                        }

                        showForm = false;
                    }
                    else
                    {
                        resultMsg.AppendLine("Account was reset, but email send failed. ");
                        if (invalidToEmailAddress)
                        {
                            resultMsg.AppendLine("The email address associated with your account was not found or did not accept the email message. ");
                            resultMsg.AppendLine("Try again later or contact the site administrator. ");
                        }
                        logMsg.AppendLine("Failed to send. ");
                        showForm = false;
                    }
                }
            }
        }

        private SNM.MailAddress GetToAddress(MembershipUser user, CCM.SiteUser siteUser)
        {
            SNM.MailAddress toAddress = null;
            if ((user != null) && !string.IsNullOrWhiteSpace(user.Email))
            {
                string fullName = null;
                if (siteUser != null)
                {
                    fullName = string.Concat(siteUser.FirstName, " ", siteUser.LastName).Trim();
                    toAddress = new SNM.MailAddress(user.Email, fullName);
                }
                if (toAddress == null)
                {
                    toAddress = new SNM.MailAddress(user.Email);
                }
            }
            return toAddress;
        }

        private SNM.MailAddress GetFromAddress()
        {
            SNM.MailAddress fromAddress = null;

            string fromEmail = SC.ConfigurationManager.AppSettings[Tags.acctResetFromEmail];
            string fromEmailName  = SC.ConfigurationManager.AppSettings[Tags.acctResetFromEmailName];
            if (!string.IsNullOrWhiteSpace(fromEmail))
            {
                if (!string.IsNullOrWhiteSpace(fromEmailName))
                {
                    fromAddress = new SNM.MailAddress(fromEmail, fromEmailName);
                }
                else
                {
                    fromAddress = new SNM.MailAddress(fromEmail);
                }
            }
            return fromAddress;
        }

#endif

        #region Status Codes
        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://go.microsoft.com/fwlink/?LinkID=177550 for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "User name already exists. Please enter a different user name.";

                case MembershipCreateStatus.DuplicateEmail:
                    return "A user name for that e-mail address already exists. Please enter a different e-mail address.";

                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";

                case MembershipCreateStatus.InvalidEmail:
                    return "The e-mail address provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                default:
                    return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
            }
        }
        #endregion
    }
}
